package easypal.brilliard.world.brilliards.communication.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import easypal.brilliard.util.TraceUtil;
import easypal.brilliard.world.brilliards.communication.CommunicationAgent;
import easypal.brilliard.world.brilliards.communication.CommunicationException;
import easypal.brilliard.world.brilliards.communication.CommunicationReceiver;
import easypal.brilliard.world.brilliards.logic.BallColor;
import easypal.brilliard.world.brilliards.logic.HitRecord;
import easypal.brilliard.world.brilliards.logic.HitStatemachine;
import easypal.brilliard.world.brilliards.logic.HitStatemachineImpl;
import easypal.brilliard.world.brilliards.logic.PlayerImpl;
import easypal.brilliard.world.brilliards.logic.PlayerViewFromHitStatemachine;

public class CommunicationReceiverServerImpl implements CommunicationReceiver {
	
	protected CommunicationServer server;

	protected Map<String, String> msg;
	
	protected CommunicationAgent agentPlayer1;
	protected CommunicationAgent agentPlayer2;
	
	protected HitStatemachine machine;
	protected PlayerViewFromHitStatemachine player1;
	protected PlayerViewFromHitStatemachine player2;
	protected HitRecord hit = new HitRecord();
	
	
	public CommunicationReceiverServerImpl(CommunicationServer server) {
		super();
		this.server = server;
	}
	
	public void setAgentOne(CommunicationAgent agent) {
		agentPlayer1 = agent;
	}
	public CommunicationAgent getAgentOne() {
		return this.agentPlayer1;
	}
	
	public void setAgentTwo(CommunicationAgent agent) {
		agentPlayer2 = agent;
	}
	public CommunicationAgent getAgentTwo() {
		return this.agentPlayer2;
	}
	
	@Override
	public synchronized void received(CommunicationAgent agent, String message) throws CommunicationException {
		TraceUtil.trace(this, "get a message: |" + message + "|");
		
		/* A client escape.*/
		if (message == null || message.equals("")) {
			
			if (agent == this.agentPlayer1) {
				TraceUtil.trace(this, "player 1 disconnect.");
				server.clientOneOffline();
			} else if (agent == this.agentPlayer2) {
				server.clientTwoOffline();
				TraceUtil.trace(this, "player 2 disconnect.");
			} else {
				TraceUtil.trace(this, "A unkown user escape");
			}
			return;
		}
		
		
		this.msg = ProtocolUtil.parseMessage(message);
		String type = msg.get("type");
		
		if (type.equals("JOIN")) {
			if (server.getState() == CommunicationServerState.wait_first) {
				// create player
				if (agent == this.agentPlayer1) {				
					player1 = new PlayerImpl();
				} else if (agent == this.agentPlayer2) {
					player2 = new PlayerImpl();
				} else {
					TraceUtil.trace(this, "Server: join error.");
				}
				// send "wait" message to this agent.
				agent.send(ProtocolUtil.makeWait() + ProtocolUtil.end);
				// One player is joined. :-)
				server.setState(CommunicationServerState.wait_second);
			} else if (server.getState() == CommunicationServerState.wait_second) {
				// create player
				if (agent == this.agentPlayer1) {				
					player1 = new PlayerImpl();
				} else if (agent == this.agentPlayer2) {
					player2 = new PlayerImpl();
				} else {
					TraceUtil.trace(this, "Server: join error.");
				}
				// send "begin" message to the two agent.
				agentPlayer1.send(ProtocolUtil.makeBegin() + ProtocolUtil.end);
				agentPlayer2.send(ProtocolUtil.makeBegin() + ProtocolUtil.end);
				
				// Two players are joined, GAME BEGINS !!!
				machine = new HitStatemachineImpl();
				machine.setPlayerOne(player1);
				machine.setPlayerTwo(player2);
				machine.begin();
				agentPlayer1.send(ProtocolUtil.makeNextTurn(machine.getTarget(), "You",
						player1.getScore(), player2.getScore(), "NotEntered", null) + ProtocolUtil.end);
				agentPlayer2.send(ProtocolUtil.makeNextTurn(machine.getTarget(), "Another", 
						player1.getScore(), player2.getScore(), "NotEntered", null) + ProtocolUtil.end);
				
				server.setState(CommunicationServerState.next_turn);
			} else {
				/* unexpected */
				TraceUtil.trace(this, "unexpected " + type);
			}

		} else if (type.equals("BALL_HIT")) {
			if (server.getState() == CommunicationServerState.next_turn) {
				// It's easy to forward message.
				agentPlayer2.send(message + ProtocolUtil.end);
				agentPlayer1.send(message + ProtocolUtil.end);
				
				server.setState(CommunicationServerState.ball_hit);
			} else {
				/* unexpected */
				TraceUtil.trace(this, "unexpected " + type);
			}
			
		} else if (type.equals("BALL_MOTION")) {
			if (server.getState() != CommunicationServerState.ball_hit) {
				/* unexpected */
				TraceUtil.trace(this, "unexpected " + type);
			}
			
			/* apply hit */
			List<String> touch = ProtocolUtil.parseCommaStringToList(msg.get("Touch"));
			List<String> entered = ProtocolUtil.parseCommaStringToList(msg.get("Entered"));
			String whiteBallEntered = msg.get("WhiteBall");
			
			this.hit.clear();
			for (String s : touch) {
				hit.touch(BallColor.parseBallColor(s));
			}
			for (String s : entered) {
				hit.enter(BallColor.parseBallColor(s));
			}
			if (whiteBallEntered != null && whiteBallEntered.equals("Entered")) {
				hit.enter(BallColor.white);
			}
			machine.hit(this.hit);
			
			/* skip ball_motion state*/
			//server.setState(CommunicationServerState.ball_motion);
			
			
			/* calculate score */
			int score = machine.getCurrentHitScore();
			if (score > 0) {
				machine.getLastPlayer().addScore(score);
			} else {
				if (player1 == machine.getLastPlayer())
					player2.addScore(-score);
				else 
					player1.addScore(-score);
			}
			
			/* reply to player */
			if (machine.isOver()) {
				// game over
				//PlayerViewFromHitStatemachine winner = null;
				if (player1.getScore() > player2.getScore()) {
					// player 1 win
					//winner = player1;
					agentPlayer1.send(ProtocolUtil.makeEnd("You") + ProtocolUtil.end);
					agentPlayer2.send(ProtocolUtil.makeEnd("Another") + ProtocolUtil.end);
				} else if (player1.getScore() < player2.getScore()){
					// player 2 win
					// winner = player2;
					agentPlayer1.send(ProtocolUtil.makeEnd("Another") + ProtocolUtil.end);
					agentPlayer2.send(ProtocolUtil.makeEnd("You") + ProtocolUtil.end);
				} else {
					// tie
					agentPlayer1.send(ProtocolUtil.makeEnd("Tie") + ProtocolUtil.end);
					agentPlayer2.send(ProtocolUtil.makeEnd("Tie") + ProtocolUtil.end);
				}
				
				// game is over.
				server.setState(CommunicationServerState.end);
		
			} else {
				// game is not over, next.
				
				// send next turn message
				PlayerViewFromHitStatemachine currentPlayer = machine.getCurrentPlayer();
				String turn = "";
				String whiteBall = "";
				BallColor target = machine.getTarget();
				
				if (machine.doesWhiteBallEnter()) {
					whiteBall = "Entered";
				} else {
					whiteBall = "NotEntered";
				}
				
				List<BallColor> colors = machine.getColorBallsThatShouldBeBack();
				System.out.println("CommunicationReceiver: recover balls: " + colors);
				
				// send to player 1
				if ( currentPlayer == player1 ) {
					turn = "You";
				} else {
					turn = "Another";
				}
				agentPlayer1.send(ProtocolUtil.makeNextTurn(machine.getTarget(), turn, 
						player1.getScore(), player2.getScore(), whiteBall, colors) + ProtocolUtil.end);
				
				// send to player 2
				if ( currentPlayer == player2) {
					turn = "You";
				} else {
					turn = "Another";
				}
				agentPlayer2.send(ProtocolUtil.makeNextTurn(machine.getTarget(), turn, 
						player1.getScore(), player2.getScore(), whiteBall, colors) + ProtocolUtil.end);
				
				server.setState(CommunicationServerState.next_turn);
			} // end if (machine.isOver)
			
			
		} else if (type.equals("CLIENT_OFF")) {
			if (agent == this.agentPlayer1) {
				server.clientOneOffline();
			} else if (agent == this.agentPlayer2) {
				server.clientTwoOffline();
			}
		} else {
			/* unexpected */
			TraceUtil.trace(this, "unexpected " + type);
		}

	}

}
